package org.dreamwork.jasmine2.web.controls.chart;

import org.dreamwork.jasmine2.annotation.ParseChildren;
import org.dreamwork.jasmine2.annotation.TemplateSupport;
import org.dreamwork.jasmine2.engine.Browser;
import org.dreamwork.jasmine2.engine.HttpContext;
import org.dreamwork.jasmine2.engine.JasmineEngine;
import org.dreamwork.jasmine2.events.EventException;
import org.dreamwork.jasmine2.jxd.InternalResourceManager;
import org.dreamwork.jasmine2.template.ITemplate;
import org.dreamwork.jasmine2.template.ITemplateControl;
import org.dreamwork.jasmine2.util.StringConverter;
import org.dreamwork.jasmine2.web.IWebContainer;
import org.dreamwork.jasmine2.web.controls.Literal;
import org.dreamwork.jasmine2.web.controls.Panel;
import org.dreamwork.jasmine2.web.controls.WebContainer;
import org.dreamwork.jasmine2.web.controls.chart.settings.ChartAlign;
import org.dreamwork.jasmine2.web.controls.chart.settings.ChartSetting;
import org.dreamwork.jasmine2.web.controls.chart.settings.ChartType;
import org.dreamwork.jasmine2.web.html.GenericHtmlControl;
import org.dreamwork.misc.BASE64Zipper;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: seth
 * Date: 2010-5-12
 * Time: 10:26:29
 */
@TemplateSupport
@ParseChildren
public abstract class AbstractChartControl extends WebContainer implements ITemplateControl {
    protected String width = "100%", height = "100%";
    protected String flashVersion = "8";
    protected String title = "Pie Chart";
    protected int titleFontSize = -1;
    protected String[] colors;
    protected String titleAlign;
    protected String textField;
    protected String url;
    protected String loadingMessage;
    protected String bgColor = "#FFFFFF";
    protected String sliceColors;
    protected String dataProvider;
    protected boolean asynchronous = true;
    protected String flashPlayerNotMatchMessage = "<strong>You need to upgrade your Flash Player</strong>";
    protected ITemplate template;

    private static final int MAX_LENGTH = 256;

    protected abstract String parseDataAsXML ();

    protected abstract String getSWFName ();

    protected abstract ChartSetting getSetting ();

    protected abstract ChartType getChartType ();

    protected abstract String getServletParameters ();

    protected AbstractChartControl () {
        viewStateEnabled = false;
    }

    @Override
    public void onDataBind (Object sender) throws EventException {
        createChildControls ();
    }

    @Override
    public void render (PrintWriter writer) throws IOException, EventException {
        for (int i = 0; i < childControls.count (); i++) {
            childControls.getControl (i).render (writer);
        }
    }

    @Override
    public void createChildControls () {
        if (!asynchronous && dataSource == null) return;

        page.registerClientScriptBlock (getClass (), "org/dreamwork/jasmine2/web/controls/chart/js/swfobject.js");
        String swf;
        try {
            swf = InternalResourceManager.getResource (getClass (), "org/dreamwork/jasmine2/web/controls/chart/swf/" + getSWFName () + ".swf");
        } catch (Exception ex) {
            throw new RuntimeException (ex);
        }

        String h = height;
        String var = id + "_movie";
        String url = page.getHttpContext ().getRequest ().getContextPath () + "/InternalResource.jxd?" + swf;

        Panel root = new Panel ();
        root.setId (id + "-root");
        for (Iterator<String> it = getStyleNames (); it.hasNext ();) {
            String name = it.next ();
            String value = getStyle (name);
            root.setStyle (name, value);
        }
        root.setStyle ("width", width.endsWith ("%") ? width : width + "px");
        root.setStyle ("height", height.endsWith ("%") ? height : height + "px");

        Browser browser = HttpContext.current ().getBrowser ();
        if (browser.isFirefox) {
            root.setStyle ("overflow-x", "hidden");
            root.setStyle ("overflow-y", "auto");
        } else {
            root.setStyle ("overflow", "hidden");
        }
//        root.setStyle ("line-height", height.endsWith ("%") ? height : height + "px");
//        root.setStyle ("text-align", "center");

        root.addControl (new Literal (flashPlayerNotMatchMessage));
        addControl (root);

        if (attrs.containsKey ("class")) root.setAttribute ("class", attrs.getProperty ("class"));
        Literal content = new Literal (String.format ("<div id='%s' style='margin-top:-40px;posotion:relation'></div>%n", id));
        root.addControl (content);

        WebContainer script = new GenericHtmlControl ("script");
        script.setAttribute ("language", "javascript");
        script.setAttribute ("type", "text/javascript");
        addControl (script);

        if (browser.isFirefox) {
            if (h.endsWith ("%")) {
                String s = String.format ("var _%s = document.getElementById ('%s');%n", root.getId (), root.getId ());
                script.addControl (new Literal (s));
                s = String.format ("document.getElementById ('%s').style.height = (_%s.clientHeight - 2) + 'px'%n", id, root.getId ());
                script.addControl (new Literal (s));
                s = String.format ("var %s = new SWFObject('%s', '%s', '%s', _%s.clientHeight - 2, '%s', '%s');%n", var, url, var, width, root.getId (), flashVersion, bgColor);
                script.addControl (new Literal (s));
            } else {
                int height = Integer.parseInt (h) - 15;
                String s = String.format ("var %s = new SWFObject('%s', '%s', '%s', '%d', '%s', '%s');%n", var, url, var, width, height, flashVersion, bgColor);
                script.addControl (new Literal (s));
            }
        } else {
            String s = String.format ("var %s = new SWFObject('%s', '%s', '%s', '%s', '%s', '%s');%n", var, url, var, width, h, flashVersion, bgColor);
            script.addControl (new Literal (s));
        }

        ChartSetting setting = getSetting ();
        if (!StringConverter.isEmpty (title)) setting.setTitle (title);
        if (titleFontSize != -1) setting.setTitleFontSize (titleFontSize);
        setting.setTitleAlign (getChartAlign (titleAlign));
        if (colors != null) setting.addAttribute ("colors", getColors ());

        String settingContent = setting.toString ();
        String data, variable;
        Literal stmt;
        try {
            if (settingContent.getBytes ().length > MAX_LENGTH) {
                String fileName = getTempFilePath (settingContent);
                String path = getContextPath () + "/FlashChartAsynchronous";
                data = String.format ("var setting = encodeURIComponent (\"%s?f=%s\");%n", path, fileName);
                variable = String.format ("%s.addVariable('settings_file', setting);%n", var);
            } else {
                data = String.format ("var setting = encodeURIComponent (\"%s\");%n", setting.toString ());
                variable = String.format ("%s.addVariable('chart_settings', setting);%n", var);
            }
            script.addControl (new Literal (data));
            script.addControl (new Literal (variable));
        } catch (IOException ex) {
            throw new RuntimeException (ex);
        }

        try {
            if (asynchronous) {
                createAsynchronousScript (script, var);
            } else {
                createSynchronousScript (script, var);
            }
        } catch (IOException ex) {
            throw new RuntimeException (ex);
        }

        script.addControl (new Literal (String.format ("%s.addParam(\"wmode\", \"opaque\");%n", var)));
        stmt = new Literal (String.format ("%s.write('%s');", var, id));
        script.addControl (stmt);
        childControlCreated = true;
    }

    private ChartAlign getChartAlign (String s_align) {
        if ("left".equalsIgnoreCase (s_align)) return ChartAlign.Left;
        if ("right".equalsIgnoreCase (s_align)) return ChartAlign.Right;
        return ChartAlign.Center;
    }

    private void createSynchronousScript (IWebContainer script, String var) throws IOException {
        String data = parseDataAsXML ();
        String content, variable;
        if (data.getBytes ().length > MAX_LENGTH) {
            String fileName = getTempFilePath (data);
            String path = getContextPath () + "/FlashChartAsynchronous";
            content = String.format ("var data = encodeURIComponent (\"%s?f=%s\");%n", path, fileName);
            variable = String.format ("%s.addVariable(\"data_file\", data);%n", var);
        } else {
            content = String.format ("var data = encodeURIComponent ('%s');%n", data);
            variable = String.format ("%s.addVariable(\"chart_data\", data);%n", var);
        }
        script.addControl (new Literal (content));
        script.addControl (new Literal (variable));
    }

    private String getContextPath () {
        HttpServletRequest request = page.getHttpContext ().getRequest ();
        StringBuffer url = request.getRequestURL ();
        String contextPath = request.getContextPath ();
        int pos = url.indexOf (contextPath) + contextPath.length ();
        return url.substring (0, pos);
    }

    private String getTempFilePath (String data) throws IOException {
        String fileName = BASE64Zipper.md5 (data);
        ServletContext application = page.getHttpContext ().getApplication ();
        File dir = (File) application.getAttribute (JasmineEngine.J2EE_TEMP_DIR);
        File tempFile = new File (dir, fileName);
        FileOutputStream fos = new FileOutputStream (tempFile);
        try {
            Writer writer = new OutputStreamWriter (fos, "utf-8");
            writer.write (data);
            writer.flush ();
        } finally {
            fos.flush ();
            fos.close ();
        }
        return fileName;
    }

    private void createAsynchronousScript (IWebContainer script, String var) throws IOException {
        String path = getContextPath () + "/FlashChart";
        Literal stmt = new Literal (String.format ("var data = encodeURIComponent ('%s?%s');%n", path, getServletParameters ()));
        script.addControl (stmt);
        stmt = new Literal (String.format ("%s.addVariable(\"data_file\", data);%n", var));
        script.addControl (stmt);
    }

    @SuppressWarnings ("unchecked")
    protected String getProviderKey () {
        ServletContext context = page.getHttpContext ().getApplication ();
        Map<String, String> pool = (Map<String, String>) context.getAttribute ("org.dreamwork.chart.pool");
        if (pool == null) {
            pool = new HashMap<String, String> ();
            context.setAttribute ("org.dreamwork.chart.pool", pool);
        }
        String providerKey = BASE64Zipper.md5 (dataProvider);
        if (!pool.containsKey (providerKey)) pool.put (providerKey, dataProvider);
        return providerKey;
    }

    public String getWidth () {
        return width;
    }

    public void setWidth (String width) {
        this.width = width.trim ();
    }

    public String getHeight () {
        return height;
    }

    public void setHeight (String height) {
        this.height = height.trim ();
    }

    public String getFlashVersion () {
        return flashVersion;
    }

    public void setFlashVersion (String flashVersion) {
        this.flashVersion = flashVersion;
    }

    public String getTitle () {
        return title;
    }

    public void setTitle (String title) {
        this.title = title;
    }

    public String getLoadingMessage () {
        return loadingMessage;
    }

    public void setLoadingMessage (String loadingMessage) {
        this.loadingMessage = loadingMessage;
    }

    public String getBgColor () {
        return bgColor;
    }

    public void setBgColor (String bgColor) {
        this.bgColor = bgColor;
    }

    public boolean isAsynchronous () {
        return asynchronous;
    }

    public void setAsynchronous (boolean asynchronous) {
        this.asynchronous = asynchronous;
    }

    public int getTitleFontSize () {
        return titleFontSize;
    }

    public void setTitleFontSize (int titleFontSize) {
        this.titleFontSize = titleFontSize;
    }

    public String getTitleAlign () {
        return titleAlign;
    }

    public void setTitleAlign (String titleAlign) {
        this.titleAlign = titleAlign;
    }

    public String getSliceColors () {
        return sliceColors;
    }

    public void setSliceColors (String sliceColors) {
        this.sliceColors = sliceColors;
    }

    public String getTextField () {
        return textField;
    }

    public void setTextField (String textField) {
        this.textField = textField;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
    public String getDataProvider () {
        return dataProvider;
    }

    public String getFlashPlayerNotMatchMessage () {
        return flashPlayerNotMatchMessage;
    }

    public void setFlashPlayerNotMatchMessage (String flashPlayerNotMatchMessage) {
        this.flashPlayerNotMatchMessage = flashPlayerNotMatchMessage;
    }

    public void setColors (String colors) {
        if (colors.indexOf (';') != -1) colors = colors.replace (';', ' ');
        if (colors.indexOf (',') != -1) colors = colors.replace (',', ' ');
        String[] a = colors.split (" ");
        if (a.length > 1) {
            this.colors = new String[a.length];
            for (int i = 0; i < a.length; i++) this.colors[i] = a[i].trim ();
        } else {
            this.colors = new String[ChartSetting.COLORS.length + 1];
            this.colors[0] = a[0].trim ();
            System.arraycopy (ChartSetting.COLORS, 0, this.colors, 1, ChartSetting.COLORS.length);
        }
    }

    public String getColors () {
        StringBuilder buf = new StringBuilder ();
        for (int i = 0; i < colors.length; i++) {
            if (i != 0) buf.append (",");
            buf.append (colors[i]);
        }
        return buf.toString ();
    }

    public void setDataProvider (String dataProvider) {
        try {
            Class clazz = Class.forName (dataProvider);
            if (!IChartDataProvider.class.isAssignableFrom (clazz)) {
                throw new RuntimeException ("Attribute \"DataProvider\" must be instance of com.zznode.irxpert.web.ui.IChartDataProvider");
            }
            this.dataProvider = dataProvider;
        } catch (Exception ex) {
            throw new RuntimeException ("Attribute \"DataProvider\" must be instance of com.zznode.irxpert.web.ui.IChartDataProvider");
        }
    }

    public void setTemplate (ITemplate template) {
        this.template = template;
    }

    public void applyTemplate () {

    }
}